using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using BuildingBasicDomain;
using MVCFramework;
using CommonTypes.Math;
using CommonTypes.Math.Geometry3D;
using System.ComponentModel;
using System.Diagnostics;

namespace BuildingStructuralElements
{
    [SnapContainmet]
    [ModelContainment(true)]
    [Serializable]
    public class BeamElement : StructuralElement,INodeLine ,ICloneable 
    {
        protected BuildingBasicDomain.CartesianCoordinateSystem3D localCoordinateSystem ;

        /// <summary>
        /// 
        /// </summary>
        private BeamType beamType;

        [System .ComponentModel .Browsable (false )]
        public BeamType BeamType
        {
            get { return beamType; }
            set { beamType = value;}
        }
        
        public Constrain GetConstraintOnNode(Node node)
        {
            return node[this];
        }

        private string  materialName;
        [System.ComponentModel.Browsable(false)]
        public string MaterialName
        {
            get { return materialName; }
            set { materialName = value; }
        }

        # region "Factories and Constructors "
        
        public static BeamElement CreateElement(IPoint  sPoint, IPoint  ePoint)
        {
            Node sNode ,eNode;

            if (sPoint is Node) sNode = (Node)sPoint;
            else sNode = Node.CreateNode(sPoint);

            if (ePoint is Node) eNode = (Node)ePoint;
            else eNode = Node.CreateNode(ePoint);

            BeamElement element;
                element = new BeamElement(sNode, eNode);

            if (IsSegmentVertical(sNode, eNode))
            {
                element.BeamType = new Beam();
            }
            else
            {
                 element.BeamType = new Column();
            }
            System.Diagnostics.Trace.Assert(element.Length() != 0, "Zero Lenght BeamElement");

            return element;
        }

        public static BeamElement CreateElement(ILine line)
        {
             NodeLine  nodeline = NodeLine.Create (line.SNode, line.ENode);
             BeamElement element;

            if (line.IsVertical())
            {     
                element = new BeamElement(nodeline );
                element.BeamType = new Column ();
            }
            else
            {   
                element = new BeamElement(nodeline );
                element.BeamType = new Beam();
            }
          //  System.Diagnostics.Trace.Assert(element.Length() != 0, "Zero Lenght BeamElement");

            return element;
        }

        protected BeamElement() : base() 
        {
            Segments = new DomainCollection<BeamSegment>(true);//Inform Collection For Models Modification On SEgments//
            ElementColor = System.Drawing.Color.Gray;
            Alpha = 0.8;

            Section = SectionManagerEC4.Inscatnce.GetSection("HEA", "100");
            

            Area area = new Area ();
            area .Add (new  Point (0,0,0));
            area.Add(new Point(1, 0, 0));
            area.Add(new Point(0, 0, 1));

           // ILine line  =  area .projectedLine  (this );

           // localCoordinateSystem  = new CartesianCoordinateSystem3D (this.SNode .V ,this.Direction,line .Direction ,CommonTypes .Math .Vector3D .CrossProduct (this.Direction,line .Direction));

            //GetSection("HEA 100");
        }
        private BeamElement(Node sNode, Node eNode): this()
        {
            this.Line = new NodeLine(sNode, eNode);
            AttachNodeLineCollectionEventHandler();
        }
        private BeamElement(NodeLine line): this()
        {
            this.Line = line; AttachNodeLineCollectionEventHandler();

        }

        private static bool IsSegmentVertical(Node sNode, Node eNode)
        {
            return (sNode.X == eNode.X) && (sNode.Y != eNode.Y) && (sNode.Z == eNode.Z);
        }

        [NonSerialized ]
        private IList<BeamSegment> segments;

        [System.ComponentModel.Browsable(false)]
        [ModelContainment]
        public IList<BeamSegment> Segments
        {
            get
            {
                if (segments == null)
                    segments = new DomainCollection<BeamSegment>(false);
                return segments;
            }

            set
            {
                segments = value; 
            }
        }

        private void AttachNodeLineCollectionEventHandler()
        {
            (this.line.NodeList as MVCFramework.IDomainCollection).OnExternalyChanged += 
                new EventHandler<EventArgs>(InternalNodes_OnExternalyHaveChanged);
        }

        # endregion

        # region " private data decleration "

        protected NodeLine line;

        [System.ComponentModel.Browsable(false)]
        [ModelContainment]
        public NodeLine Line
        {
            set
            {
                if (this.line != null)
                {
                    (this.line.SNode as Node).OnMove += new EventHandler<EventArgs>(BeamElement_OnMove);
                    (this.ENode as Node).OnMove += new EventHandler<EventArgs>(BeamElement_OnMove);
                }

                (value.SNode as Node).OnMove += new EventHandler<EventArgs>(BeamElement_OnMove);
                (value.ENode as Node).OnMove += new EventHandler<EventArgs>(BeamElement_OnMove);

                line = value;

            }
            get
            {
                if (line == null)
                    line = new NodeLine();
                return line;
            }
        }

        void BeamElement_OnMove(object sender, EventArgs e)
        {
            ChechChangeType();
        }

        protected Section _section;

        [ModelContainment]        
        public Section Section
        {
            get { return _section; }
            set { _section = value;
                if( _section != null)
            _section.NeedsUpdate = true;
        }
        }

        # endregion

        public void RotateToPoint(IPoint point, Vector3D angles)
        {
            foreach (Node node in this)
                node.RotateToPoint(point, angles);
        }

        public void RotateToZero(Vector3D  angles)
        {
           foreach (Node  node in this)
               node.RotateToZero(angles);         
        }

        # region " IContainer implementation "

        public Node this[int i]
        {
            get
            {
                return Line[i];
            }
            set
            {
                if (i >= 0 && i < Count)
                {
                    if (value is IPoint)
                    {
                        try
                        {
                            if (value is Node)
                            {
                                Line[i] = (value);
                            }
                            else
                            {
                                Node node = (Node)value;
                                Line[i] = node;
                            }

                        }
                        catch (Exception exp) { throw new StructuralException ("Cannot Add Point in Beam ",exp); }
                    }
                }
            }
        }

        /// <summary>
        /// Method Add
        /// </summary>
        /// <returns>A bool</returns>
        /// <param name="point">A  T</param>
        public bool Add(Node point)
        {
            // TODO: Implement this method
            if (point is IPoint)
            {
                try
                {
                    bool validation = false ;

                    if (point is Node)
                    {
                        validation = Line.Add(point);
                    }
                    else
                    {
                        Node node = (Node)point;
                        validation = Line.Add(node);

                    }
                    if (validation == true)
                    {
                        DefineSegments();
                      //  validation = AddSegment((Node)point);
                        return validation;
                    }

                }
                catch (Exception exp) { throw new StructuralException ("Cannot add this type "+
                    point .GetType ()+" to BeamElement",exp); }
            }
            return false;

        }

        /// <summary>
        /// Method Remove
        /// </summary>
        /// <returns>A bool</returns>
        /// <param name="point">A  T</param>
        public bool Remove(Node point)
        {
           return  Line.Remove(point);
        }

        /// <summary>
        /// Method Contains
        /// </summary>
        /// <returns>A bool</returns>
        /// <param name="point">A  T</param>
        public bool Contains(Node point)
        {

            return Line.Contains(point);
        }
        [System.ComponentModel.Browsable(false)]
        /// <summary>
        /// Method Count
        /// </summary>
        /// <returns>An int</returns>
        public int Count
        {
            get
            {
                return Line.Count;
            }
        }
            
        /// <summary>Returns an enumerator that iterates through a collection.</summary>
        /// <returns>An <see cref="T:System.Collections.IEnumerator"></see> object that can
        /// be used to iterate through the collection.</returns>
        /// <filterpriority>2</filterpriority>
        public IEnumerator GetEnumerator()
        {
            return Line.GetEnumerator();
        }

        # endregion

        # region " ILine Implementation "

        [ModelContainment]
        //[Category ("Nodes"),Description ("Starting Node")]
        [System.ComponentModel.Browsable(false)]
        public IPoint SNode
        {
            get
            {

                return line.SNode;
            }
            set
            {
                line.SNode = value;
            }
        }
        [System.ComponentModel.Browsable(false)]
        [ModelContainment]
       // [Category("Nodes"), Description("Ending Node")]
        public IPoint ENode
        {
            get
            {
                // TODO
                return line.ENode;
            }
            set
            {
                line.ENode = value;
            }
        }

        public Vector3D Direction
        {
            get
            {
                // TODO
                return Line.Direction;
            }
        }

        /// <summary>
        /// Method Perpendicular
        /// </summary>
        /// <returns>A Vector3D</returns>
        /// <param name="point">An IPoint</param>
        public Vector3D Perpendicular(IPoint point)
        {
            // TODO: Implement this method
            return Line.Perpendicular(point);
        }

        /// <summary>
        /// Method Length
        /// </summary>
        /// <returns>A double</returns>
        public double Length()
        {
            // TODO: Implement this method
            return Line.Length();
        }

        /// <summary>
        /// Method ProjectionOnLine
        /// </summary>
        /// <returns>An IPoint</returns>
        /// <param name="point">An IPoint</param>
        public IPoint ProjectionOnLine(IPoint point)
        {
            // TODO: Implement this method
            return Line.ProjectionOnLine(point);
        }

        /// <summary>
        /// Method IntersectsLine
        /// </summary>
        /// <returns>An IPoint</returns>
        /// <param name="beamelement2">An ILine</param>
        public IPoint IntersectsLine(ILine line ,CommonTypes .Geometry .Type_of_Intersection  typeOfIntersection)
        {
            // TODO: Implement this method
            return this.Line.IntersectsLine(line,typeOfIntersection);
        }

        /// <summary>
        /// Method IntersectsPlane
        /// </summary>
        /// <returns>An IPoint</returns>
        /// <param name="plane">An IPlane</param>
        public IPoint IntersectsPlane(IPlane plane)
        {
            // TODO: Implement this method
            return Line.IntersectsPlane(plane);
        }

        /// <summary>
        /// Method ContainsPointOpen
        /// </summary>
        /// <returns>A bool</returns>
        /// <param name="point">An IPoint</param>
        public bool ContainsPointOpen(IPoint point)
        {
            // TODO: Implement this method
            return Line.ContainsPointOpen(point);
        }

        /// <summary>
        /// Method DistancePoint
        /// </summary>
        /// <returns>A double</returns>
        /// <param name="point">An IPoint</param>
        public double DistancePoint(IPoint point)
        {
            // TODO: Implement this method
            return Line.DistancePoint(point);
        }

        /// <summary>
        /// Method IsVertical
        /// </summary>
        /// <returns>A bool</returns>
        public bool IsVertical()
        {
            // TODO: Implement this method
            return Line.IsVertical();
        }

        /// <summary>
        /// Method IsHorizontal
        /// </summary>
        /// <returns>A bool</returns>
        public bool IsHorizontal()
        {
            // TODO: Implement this method
            return Line.IsHorizontal();
        }

        public double[] getAngles()
        {
            return Line.getAngles();
        }

        public IPoint GetMiddle()
        {
            return Line.GetMiddle();
        }

        public INodeLine[] SplitLineToNodes()
        {
            if (this.Count < 2) return null;

            BeamSegment [] beams = new BeamSegment[this.Count - 1];
            INodeLine[] lines = Line.SplitLineToNodes();

            for (int i = 0; i < this.Count - 1; i++)
                beams[i] = (GetPrototype(lines[i] as NodeLine) as BeamSegment);
            return beams;
        }

        private object GetPrototype(NodeLine line)
        {
            BeamElement newelement = new BeamSegment ();
            newelement.Line = line;
           // newelement.Section = this.Section;
            newelement.beamType = beamType;

            return newelement;
        }

        # endregion

        # region " IModiafiable Implementation"

        public override bool Move(CommonTypes.Vector vector)
        {
            Section.NeedsUpdate = true;

            bool validation =  line.Move(vector);

            ChechChangeType();

            return validation;

        }

        public override bool IsSelected
        {
            get
            {
                return base.IsSelected;
            }
            set
            {
                for (int i = 0; i < this.Count; i++)
                {
                    Node node = this[i];
                    node.IsSelected = value;
                }
                if (Segments != null)
                {
                  //  foreach (BeamSegment segment in Segments)
                      //  segment.IsSelected = value;
                }
                base.IsSelected = value;
            }
        }


        private void ChechChangeType()
        {
            if (Line.IsVertical() && this.BeamType is Beam)
                this.BeamType = new Column();
            if (!line.IsVertical() && this.BeamType is Column)
                this.BeamType = new Beam();
        }

        public override bool Move(double dx, double dy, double dz)
        {
            return Line.Move(dx, dy, dz);

        }

        # endregion

        # region " General Declerations"

        public bool isOnGround()
        {
            if ((this.SNode.Y == 0) || (this.ENode.Y == 0))
                return true;
            return false;
        }

        public List<Node> NodesOnGround()
        {
            List<Node> nodes = new List<Node>();

            if (SNode.Y == 0)
                nodes.Add(SNode as Node);
            if (ENode.Y == 0)
                nodes.Add(ENode as Node);

            return nodes;
        }

        public override string ToString()
        {
           return beamType.GetType ().ToString ()+" ID : " + id +" snode "+SNode.ToString() + " enode " + ENode .ToString ();
        }

        public override string Name
        {
            get
            {
                return base.Name;
            }
            set
            {
                base.Name = value;
            }
        }

        private bool DefineSegments()
        {
            if (segments == null) { segments = new DomainCollection<BeamSegment>(false); }
            segments.Clear();
            ILine[] lines = SplitLineToNodes();
            if (lines != null)
                foreach (BeamSegment segment in lines)
                {
                    Segments.Add(segment);
                    System.Diagnostics.Debug.Assert(segment.Length() != 0, "Segment with Zero length");
                }
            return true;
        }

        [Obsolete ("Not finished ")]
        private bool AddSegment(Node node)
        {
            if( this.Contains (node ))
            if (Segments.Count == 0) DefineSegments();

            foreach (BeamSegment segment in this.Segments )
                if(segment .ContainsPointOpen (node))
                {
                    segment.ENode = node;
                   BeamSegment newsegment  =  segment.GetPrototype(new NodeLine((Node)segment.ENode, node)) as BeamSegment ;
                   System.Diagnostics.Debug.Assert(newsegment.Length() != 0, "Zero Lenght Segement ");

                   this.segments.Add(newsegment);
                   return true;
                }
            return false;

        }
        [Obsolete("Not finished ")]
        private bool RemoveSegment(Node node)
        {
            BeamSegment segment_to_remove;
            BeamSegment segment_to_merge;
            int index=-1;

            for (int i = 0; i < segments.Count; i++)
                if (segments[i].ENode == node)
                {
                    index = i;
                    if (i == segments.Count) throw new StructuralException("Node is Beam End Node");
                }
            if (index != -1)
            {
                segment_to_merge = segments[index];
                segment_to_remove = segments[index + 1];
                segment_to_merge.ENode = segment_to_remove.ENode;
                Segments.Remove(segment_to_remove);
                return true;
            }

            return false;
        }

        # endregion

        #region ICloneable Members

        public object Clone()
        {
            BeamElement newelement = new BeamElement(this.Line);
            newelement.Section = this.Section;
            return newelement;
        }

        #endregion

        #region "Operator Implementation "

        public static bool operator ==(BeamElement beamelemnt1, BeamElement beamelement2)
        {
            object o1 = beamelemnt1;
            object o2 = beamelement2;

            if (o1 == null && o2 == null) return true;
            if (o1 == null) return false;
            if (o2 == null) return false;

            if (HasSameNodes(beamelemnt1, beamelement2))
                return true;
            else
                return false;

        }

        private static bool HasSameNodes(BeamElement beamelemnt1, BeamElement beamelement2)
        {
            return (((Point )beamelemnt1.ENode == (Point )beamelement2.ENode &&(Point ) beamelemnt1.SNode == (Point)beamelement2.SNode) ||
             ((Point)beamelemnt1.SNode == (Point)beamelement2.ENode &&(Point ) beamelement2.SNode ==(Point) beamelemnt1.ENode));
            
        }

        public static bool operator !=(BeamElement line1, BeamElement line2)
        {
            return !(line1 == line2);
        }

        public override bool Equals(object obj)
        {
            if (obj is BeamElement)
                return (bool)(this == (BeamElement)obj);
            else return false;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        int IComparer<IPoint>.Compare(IPoint x, IPoint y)
        {
            if (x.distancePoint(this.Line . SNode) > y.distancePoint(this.Line .SNode))
                return 1;
            else return -1;
        }

        #endregion

        protected void InternalNodes_OnExternalyHaveChanged(object sender, EventArgs args)
        {
        //   object removedNode = ((DomainCollectionChangedEventArgs<object>)args).ChangedItem;

         //  RemoveSegment (removedNode as Node  );
            DefineSegments();

        }

        public IEnumerable <IPoint> ExtremePoints()
        {
            yield return this.SNode;
            yield return this.ENode;
        }
        public bool IsPointExtreme(IPoint point)
        {
            foreach (IPoint extremepoint in ExtremePoints())
                if (point == extremepoint) return true;
            return false;

        }

        #region INodeLine Members


        public INodeLine[] SplitLine(int number)
        {
            Line.SplitLine(number);
            DefineSegments();
            return null;
        }

        #endregion
        [System.ComponentModel.Browsable(false)]
        public override double AngleX
        {
            get
            {
                return base.AngleX;
            }
            set
            {
                this.RotateToPoint(this.GetMiddle(), new Vector3D(value, 0, 0));
            }
        }
        [System.ComponentModel.Browsable(false)]
        public override double AngleY
        {
            get
            {
                return base.AngleY;
            }
            set
            {
                this.RotateToPoint(this.GetMiddle(), new Vector3D(0, value, 0));
            }
        }
        [System.ComponentModel.Browsable(false)]
        public override double AngleZ
        {
            get
            {
                return base.AngleZ;
            }
            set
            {
                this.RotateToPoint(this.GetMiddle(), new Vector3D(0, 0, value));
            }
        }
        [System.ComponentModel.Browsable(false)]
        public override double Dx
        {
            get
            {
                return base.Dx;
            }
            set
            {
                this.Move(new CommonTypes.Vector(value, 0, 0));
            }
        }
        [System.ComponentModel.Browsable(false)]
        public override double Dy
        {
            get
            {
                return base.Dy;
            }
            set
            {
                this.Move(new CommonTypes . Vector(0, value, 0));
            }
        }
        [System.ComponentModel.Browsable(false)]
        public override double Dz
        {
            get
            {
                return base.Dz;
            }
            set
            {
                this.Move(new CommonTypes.Vector(0, 0, value));
            }
        }

        #region ILine Members


        public bool ContainsPointClose(IPoint point)
        {
           return  line.ContainsPointClose(point);
        }

        #endregion
    }

    [Serializable ]
    public class BeamType
    {

    }


}
